﻿/* 
 * yaku
 * http://code.google.com/p/yaku/
 * 
 * Copyright 2011, Bryan McKelvey
 * Licensed under the MIT license
 * http://www.opensource.org/licenses/mit-license.php
 */

using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Data.SqlClient;
using System.Windows;
using System.Windows.Controls;
using System.Xml;

namespace yaku
{
    /// <summary>
    /// A class for interacting with the dictionary database.
    /// </summary>
    public partial class Edict
    {
        public enum SearchType { Exactly, BeginsWith, EndsWith, Contains };
        public enum EntryCategory { None, English, Kanji, Furigana };
        private List<EdictEntry> entries = new List<EdictEntry>();

        private DataGrid resultsGrid;
        private TextBox searchBar;
        private TextBlock statusBarText;
        private SearchType searchType = (SearchType)Properties.Settings.Default.SearchType;
        private int maxResults = 100;

        /// <summary>
        /// A class for interacting with the dictionary database.
        /// </summary>
        /// <param name="searchBar">The main window's search bar.</param>
        /// <param name="resultsGrid">The main window's dictionary results grid.</param>
        /// <param name="statusBarText">The main window's status bar text control.</param>
        public Edict(ref TextBox searchBar, ref DataGrid resultsGrid, ref TextBlock statusBarText)
        {
            string dictPath = Properties.Settings.Default.DictPath;
            this.searchBar = searchBar;
            this.resultsGrid = resultsGrid;
            this.statusBarText = statusBarText;
            Transliterator.Build();
            if (File.Exists(dictPath))
            {
                Load(dictPath, true);
            }
        }

        /// <summary>
        /// Manually loads an XML file into the dictionary database.
        /// </summary>
        public void ManualLoad()
        {
            Microsoft.Win32.OpenFileDialog ofd = new Microsoft.Win32.OpenFileDialog()
            {
                DefaultExt = "",
                Filter = "Dictionary files|*.*"
            };
            if (ofd.ShowDialog() == true)
            {
                Load(ofd.FileName);
                Properties.Settings.Default.DictPath = ofd.FileName;
            }
        }

        /// <summary>
        /// Loads an XML file into the Edict object.
        /// </summary>
        /// <param name="fileName">The path to the XML file.</param>
        private void Load(string fileName)
        {
            Load(fileName, false);
        }

        /// <summary>
        /// Loads an XML file into the Edict object.
        /// </summary>
        /// <param name="fileName">The path to the XML file.</param>
        /// <param name="suppressStatus">Whether to suppress status updates. Defaults to false.</param>
        private void Load(string fileName, bool suppressStatus)
        {
            EntryCategory category = EntryCategory.None;
            XmlTextReader reader = new XmlTextReader(fileName);

            int counter = 0;
            entries.Clear();
            while (reader.Read())
            {
                switch (reader.NodeType)
                {
                    case XmlNodeType.Element:
                        switch (reader.Name)
                        {
                            case "entry":
                                entries.Add(new EdictEntry());
                                break;
                            case "gloss":
                                if (!reader.HasAttributes)
                                {
                                    category = EntryCategory.English;
                                }
                                break;
                            case "keb":
                                category = EntryCategory.Kanji;
                                break;
                            case "reb":
                                category = EntryCategory.Furigana;
                                break;
                        }
                        break;
                    case XmlNodeType.EndElement:
                        entries.Last().Close();
                        break;
                    case XmlNodeType.Text:
                        switch (category)
                        {
                            case EntryCategory.English:
                                entries.Last().EnglishList.Add(reader.Value);
                                break;
                            case EntryCategory.Kanji:
                                entries.Last().KanjiList.Add(reader.Value);
                                break;
                            case EntryCategory.Furigana:
                                entries.Last().FuriganaList.Add(reader.Value);
                                break;
                        }

                        category = EntryCategory.None;
                        break;
                }

                if (!suppressStatus && ++counter % 100000 == 0)
                {
                    SetStatus("Read {0:0.0} million lines...", (double)counter / 1000000);
                }
            }

            if (!suppressStatus) SetStatus("Found {0:n0} entries", entries.Count);
        }

        /// <summary>
        /// Sets the text contents of the status bar.
        /// </summary>
        /// <param name="statusFormat">A composite format string to display in the status bar.</param>
        /// <param name="args">An object array that contains zero or more objects to format.</param>
        private void SetStatus(string statusFormat, params object[] args)
        {
            SetStatus(string.Format(statusFormat, args));
        }

        /// <summary>
        /// Sets the text contents of the status bar.
        /// </summary>
        /// <param name="status">A string to display in the status bar.</param>
        private void SetStatus(string status)
        {
            statusBarText.Text = status;
            statusBarText.Refresh();
        }

        /// <summary>
        /// Searches the dictionary based on the content of the previously specified search bar and search type, returning the results to the results grid.
        /// </summary>
        public void Search()
        {
            string searchString = string.Format("{0}{1}{2}",
                ((searchType == SearchType.BeginsWith || searchType == SearchType.Exactly) ? "|" : ""),
                searchBar.Text,
                ((searchType == SearchType.EndsWith || searchType == SearchType.Exactly) ? "|" : ""));

            resultsGrid.ItemsSource = (from e in entries
                                       where e.SearchText.Contains(searchString)
                                       select e).Take(maxResults);
        }

        #region Properties
        public int MaxResults
        {
            get { return maxResults; }
            set { maxResults = value; }
        }

        public TextBox SearchBar
        {
            get { return searchBar; }
        }

        public DataGrid ResultsGrid
        {
            get { return resultsGrid; }
        }

        public TextBlock StatusBarText
        {
            get { return statusBarText; }
        }

        public SearchType Type
        {
            get { return searchType; }
            set
            {
                searchType = value;
                Properties.Settings.Default.SearchType = (int)value;
            }
        }
        #endregion
    }
}
